// 快捷形式：wx.http.get/post/put/delete(url, data, headers)
// 通用形式：wx.http({ method, url, data, headers })
// 请求拦截器：
// wx.http.interceptors.request.use((config) => { return config })
// 响应拦截器：
// wx.http.interceptors.response.use((res) => { return res })
// 基准地址：
// wx.http.baseURL = 'xxx'

function http(config) {
  return new Promise((resolve, reject) => {
    // http.baseURL = 'http://localhost:8080'
    // http.baseURL = 'http://localhost:8080/'

    // 如果不是以http开头，才需要拼接基地址
    if (!config.url.startsWith('http')) {
      // 看 baseURL 是不是以 / 结尾，如果是，就先把 / 截了
      // 再看 config.url是不是以 / 开头，如果是，就先把 / 截了
      // 最后，保证 baseURL 屁股肯定没有 /，保证 config.url 头部肯定没有 /
      // 然后，自己去拼接 /
      if (http.baseURL.endsWith('/')) {
        http.baseURL = http.baseURL.slice(0, -1)
      }
      if (config.url.startsWith('/')) {
        config.url = config.url.slice(1)
      }
      config.url = http.baseURL + '/' + config.url
    }
    // if (http.baseURL) {
    //   if (config.url.startsWith('/')) {
    //     if (http.baseURL.endsWith('/')) {
    //       config.url = http.baseURL.slice(0, -1) + config.url
    //     } else {
    //       config.url = http.baseURL + config.url
    //     }
    //   } else {
    //     if (http.baseURL.endsWith('/')) {
    //       config.url = http.baseURL + config.url
    //     } else {
    //       config.url = http.baseURL + '/' + config.url
    //     }
    //   }
    // }

    config = http.interceptors.request._cb(config)

    if (!config.method) {
      config.method = 'GET'
    }

    // 转换headers成header，因为微信的wx.request需要header，而axios需要headers（用户习惯）
    if (config.headers) {
      config.header = config.headers
      delete config.headers
    }

    // if (config.method === 'GET') {
    //   const params = new URLSearchParams()
    //   for (const [key, value] of Object.entries(config.data)) {
    //     params.append(key, value)
    //   }
    //   config.url += '?' + params.toString()
    //   delete config.data
    // }

    wx.showLoading({
      title: '加载中...',
      mask: true,
    })

    // 真正的发出去请求
    wx.request({
      ...config,
      success(res) {
        res = http.interceptors.response._cb(res, config)
        // 如果res是promise，resolve内部会自动区分，会等待其状态改变再执行，受res的影响，res成功，当前Promise就成功，res失败，当前Promise就失败
        resolve(res)
      },
      fail(err) {
        err = http.interceptors.response._cb(err, config)
        // 如果err是promise，resolve内部会自动区分，会等待其状态改变再执行，受err的影响，err成功，当前Promise就成功，err失败，当前Promise就失败
        // 注意：如果err是promise，reject不受err影响，给啥就是啥
        if (err instanceof Promise) {
          resolve(err)
        } else {
          reject(err)
        }
      },
      complete() {
        wx.hideLoading()
      },
    })
  })
}

http.get = function (url, data, headers) {
  return http({
    method: 'GET',
    url,
    data,
    headers,
  })
}

http.post = function (url, data, headers) {
  return http({
    method: 'POST',
    url,
    data,
    headers,
  })
}

http.put = function (url, data, headers) {
  return http({
    method: 'PUT',
    url,
    data,
    headers,
  })
}

http.delete = function (url, data, headers) {
  return http({
    method: 'DELETE',
    url,
    data,
    headers,
  })
}

http.uploadFile = function (filePath, type = 'avatar') {
  return new Promise((resolve, reject) => {
    wx.showLoading({
      title: '上传中...',
      mask: true,
    })

    const config = {
      url: wx.http.baseURL + '/upload',
      filePath,
      name: 'file',
      formData: {
        type,
      },
      header: {
        Authorization: `Bearer ${getApp().global.token.token}`,
      },
    }

    wx.uploadFile({
      ...config,
      success: (res) => {
        resolve(wx.http.interceptors.response._cb(res, config))
      },
      fail: (...args) => {
        reject(args)
      },
    })
  }).finally(() => {
    wx.hideLoading()
  })
}

http.interceptors = {
  request: {
    // 用来存储请求拦截器函数
    // 命名规范：下划线开头的，表示私有的，不想让别人看到的东东
    _cb: (config) => config,
    use(cb) {
      this._cb = cb
    },
  },
  response: {
    // 用来存储响应拦截器函数
    // eslint-disable-next-line no-unused-vars
    _cb: (res, config) => res,
    use(cb) {
      this._cb = cb
    },
  },
}

http.baseURL = 'https://live-api.itheima.net'

http.interceptors.request.use((config) => {
  const defHeaders = {}
  const tokenData = getApp().global.token

  // 注意：之前的注入token是固定的，用户不用配，配了也没用，会导致refreshToken设置不上去，所以不能使用之前的注入方式
  // if (tokenData) {
  //   config.headers.Authorization = `Bearer ${tokenData.token}`
  // }

  if (tokenData) {
    defHeaders.Authorization = `Bearer ${tokenData.token}`
  }

  config.headers = Object.assign({}, defHeaders, config.headers)

  return config
})

// 响应拦截器
http.interceptors.response.use((res, config) => {
  // 有可能是res，也有可能是err

  const { token } = getApp().global

  // 如果响应状态码为401，并且不是刷新token请求，说明token过期
  if (res.statusCode === 401 && !config.url.endsWith('/refreshToken')) {
    // 有token就去刷
    if (token) {
      return (
        wx
          .http({
            method: 'POST',
            url: '/refreshToken',
            headers: {
              Authorization: `Bearer ${token.refreshToken}`,
            },
          })
          // catch 就是 then 的变体，内部也是用 then 实现的
          .catch(() => {
            // 刷新token失败的逻辑
            wx.redirectTo({
              url: '/pages/login/index?redirectURL=' + getCurrentPages().pop().route,
            })
            wx.utils.toast('登录已过期，请重新登录！')
            // 后续的逻辑，不用走了，因为已经跳到登录去了
            return new Promise(() => {})
          })
          .then((res) => {
            console.log('[ 刷新token成功 ] >', res.data)

            // 用最新的token数据 替换旧的token数据
            wx.setStorageSync('token', res.data)
            getApp().global.token = res.data

            // 重新发上一次失败的请求之前，先把请求配置对象中的header转换成headers，因为我们发请求的时候用的是axios规范（headers），而用wx.request发出去时已经转换成了（header），由于这里又要重发，于是就需要先再将header转成headers
            config.headers = config.header
            // 然后删除header
            delete config.header
            // 然后再删除headers.Authorization，因为上次的token已经过期了
            delete config.headers.Authorization

            // 然后再用处理好的请求配置对象，重新发出上一次失败的请求，这次请求也会经过请求拦截器（注入token），此时的token已经是最新的了
            if (config.url.endsWith('/upload')) {
              // 如果是上传文件，就用 wx.http.uploadFile 去重发
              return wx.http.uploadFile(config.filePath, config.formData.type)
            } else {
              return wx.http(config)
            }
          })
        // 这个 catch 进去的入口有两个，① 刷新token失败，② 重新新发上次失败的请求又失败
        // .catch(() => {})
      )
    }
    // 没token，就去登录
    else {
      wx.redirectTo({
        url: '/pages/login/index?redirectURL=' + getCurrentPages().pop().route,
      })
      wx.utils.toast('登录已过期，请重新登录！')
      // 后续的逻辑，不用走了，因为已经跳到登录去了
      return new Promise(() => {})
    }
  }
  // 否则，请求响应正常
  else {
    // 提取后端返回的响应数据
    // const data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data

    // 尝试JSON.parse，能parse就parse，parse不了会报错，要抓住，不能干扰后面的代码执行，抓了不用干啥，保持原样就行了
    // 上传图片的时候res.data返回的是JSON格式的字符串，为了让响应拦截器兼容上传图片，所以，加了这个代码
    let data = res.data

    try {
      data = JSON.parse(data)
      // eslint-disable-next-line no-empty
    } catch (err) {}

    // 检测接口是否调用成功
    if (data.code !== 10000) {
      wx.utils.toast(data.message)
      // 返回失败的Promise，让后续链式操作失败
      return Promise.reject(data)
    }

    // 成功，就直接返回data
    return data
  }
})

wx.http = http

export default http
